package com.helmlabs.grouper.accounts.dao;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.core.simple.ParameterizedRowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

import com.helmlabs.grouper.accounts.model.Role;

public class RoleDaoJdbc extends JdbcDaoSupport implements RoleDao {

	private Log log = LogFactory.getLog(getClass());
	
	/**
	 * Get a role by using its unique identifier number
	 * @param long id of the group
	 * @return com.helmlabs.grouper.accounts.model.Role
	 */
	public Role get(Long id) {
		String sql = "SELECT * FROM roles r WHERE r.id = ? LIMIT 1";
		Role role = (Role)this.getJdbcTemplate().queryForObject(sql, new Object[]{id}, new RoleRowMapper());
		return role;
	}
	
	/**
	 * Get all the roles who have a parent id and are of a specific type
	 * and have a parentid of x.  If you would like to get the top level
	 * Roles, then send a null value for parentid.
	 * @param type
	 * @param parentid
	 * @return
	 */
	public List<Role> getAllRolesByTypeAndParent(String type, Long parentid){
		List<Role> roles = new ArrayList();
		String sql = "";
		Object[] args;
		if (parentid == null){
			sql = "SELECT * FROM roles r WHERE r.type = ? and r.parent is null";
			args = new Object[]{type};
			roles = this.getJdbcTemplate().query(sql, args, new RoleRowMapper());
		}
		else {
			sql = "SELECT * FROM roles r WHERE r.type = ? and r.parent = ?";
			args = new Object[]{type, parentid};
			roles = this.getJdbcTemplate().query(sql, args, new RoleRowMapper());
		}
		return roles;
	}
	
	public List<Role> getAllActiveByUserId(Long userid){
		String sql = "SELECT * FROM roles r, user_roles ur WHERE ur.role_id = r.id AND ur.user_id = ?";
		return this.getJdbcTemplate().query(sql, new Object[]{userid}, new RoleRowMapper());
	}

	public Role save(Role role) {
		// TODO Auto-generated method stub
		return null;
	}	
		
	private class RoleRowMapper implements ParameterizedRowMapper {
		public Object mapRow(ResultSet rs, int index) throws SQLException {
			Role role = new Role();
			role.setId(rs.getLong("id"));
			role.setName(rs.getString("name"));
			if (rs.getString("type").equals(Role.USER_ROLES)){
				role.setType(Role.USER_ROLES);
			} else {
				role.setType(Role.COMPANY_ROLES);
			}
			role.setActive(rs.getBoolean("active"));
			role.setDescription(rs.getString("description"));
			if (rs.getString("parent")!=null){
				role.setParent(get(rs.getLong("parent")));
			} else {
				role.setParent(null);
			}
			return role;
		}
		
		
	}

	/**
	 * Retrieve all the active roles for a specific object.  This
	 * will only return the roles that are directly associated with
	 * the user, not the entire tree of roles.
	 * 
	 * @param roleType
	 * @param objectId
	 * @return List<com.helmlabs.grouper.accounts.model.Role>
	 */
	public List<Role> getActiveRolesForObject(String roleType, Long objectId){
		log.debug("retreiving the active roles for the object " + objectId);
		String sql = "select * from roles r, roles_associations ra where r.id = ra.role_id and ra.active=1 and r.type = ra.type and r.type = ? and ra.object_id = ? ";
		Object[] args = {roleType, objectId};
		return this.getJdbcTemplate().query(sql, args, new RoleRowMapper());
	}
	
	/**
	 * Associate a role with an object. First a check will be performed to make
	 * sure the user is not directly associated with the role already.
	 * If the user is already associated with a role, nothing will happen, 
	 * otherwise a record will be placed into the roles_associations table
	 * @param String roleType
	 * @param Long objectId
	 * @param Long roleId
	 * @return true if successfully placed in group
	 * 
	 */
	public boolean activateObjectInRole(String roleType, Long objectId,
			Long roleId) {
		if (!isObjectInRole(roleType, objectId, roleId)) {
			String sql = "insert into roles_associations (role_id, object_id, active, created_on, modified_on, type) VALUES (?,?,?,?,?,?)";
			Object[] args = {roleId, objectId, 1, new Date(), new Date(), roleType};
			int status = this.getJdbcTemplate().update(sql, args);
			if (status>0)
				return true;
			return false;
		}
		return true;
	}

	/**
	 * Update the roles_associations table to ensure that the object
	 * is no longer associated with a given roleId.  If the object is
	 * not already associated with the role that is used in the parameter, 
	 * the method will complete without issue.
	 * @param String roleType
	 * @param Long objectId
	 * @param Long roleId
	 */
	public boolean deactivateObjectInRole(String roleType, Long objectId,
			Long roleId) {
		String sql = "update roles_associations set active = 0, modified_on = now() where role_id=? and object_id=? and type=?";
		Object[] args = {roleId, objectId, roleType};
		int status = this.getJdbcTemplate().update(sql, args);
		if (status>0){
			return true;
		}
		return false;
	}
	
	/**
	 * Check to see if an object is directly associated with
	 * a role.  This will not perform a recursive check, it will
	 * only check to see if the user is in that specific role.
	 * @param String roleType
	 * @param Long objectId
	 * @param Long roleId
	 */
	public boolean isObjectInRole(String roleType, Long objectId, Long roleId){
		String sql = "select count(*) as total from roles_associations where role_id=? and object_id=? and type=? and active=1";
		Object[] args = {roleId, objectId, roleType};
		int count = this.getJdbcTemplate().queryForInt(sql, args);
		if (count>0){
			return true;
		}
		return false;
	}
	
	public boolean isObjectInRoleRecursive(String roleType, Long objectId, Long roleId){
		
		List<Role> roles = this.getActiveRolesForObject(roleType, objectId);
		log.debug("++++++++ found that the object " + objectId + " has " + roles.size() + "number of roles " );
		for (Role role : roles){			
			String path = getRolePathUpward(role.getId().toString());
			log.debug("+++++++++ testing if " + role.getId() + " is somewhere in " + path);
			if (path.equals(roleId))
				return true;
			if (path.startsWith(roleId + ":"))
				return true;
			if (path.endsWith(":" + roleId))
				return true;
			if (path.contains(":" + roleId + ":"))
				return true;
		}
		return false;
	}
	
	public String getRolePathUpward(String path){
		String[] tempId = path.split(":");
		try {
			Long parentId = this.getJdbcTemplate().queryForLong("SELECT parent FROM roles r WHERE r.id = ?", new Object[]{tempId[0]});
			if (parentId!=0){
				path = parentId + ":" + path;
				return getRolePathUpward(path);
			}
		}catch (Exception ex){
		}
		return path;
	}

}
